package cheesymock;

import java.lang.reflect.*;
import static cheesymock.MethodUtil.*;

/**
 * <h1>CheesyMock</h1>
 *
 * CheesyMock is a small and simple library for creating mock objects
 * from Java interfaces. It has but one design goal:
 *
 * <blockquote><i>The user should have to type as little as possible.</i>
 * </blockquote>
 *
 * <h2>A Use Case Example</h2>
 *
 * <p> Let's say you're writing an application and want to write some
 *    unit tests. For one of your tests you need to supply an instance
 *    of {@link javax.servlet.http.HttpServletRequest}, which puts you
 *    in kind of pickle. You have no concrete implementation of that
 *    class, since that's usually provided by a sevlet container. So
 *    what do you do? Well, you <i>could</i> write your own simple
 *    implementation of HttpServletRequest and use that in your test
 *    case. It's not that hard, especially if you're using one of the
 *    modern IDEs that can generate most of the code for you.</p>
 *
 * <p> It seems a bit wasteful, though. You know for a fact that your
 *    test will only ever call a single method (let's say {@link
 *    javax.sevlet.http.HttpServletRequest#getRequestURI()}) and only
 *    requires a trivial return value (let's say
 *    "www.myserver.com/myApp/myServlet/"). Why should you have to
 *    create a big honkin' class just for that?</p>
 *
 * <p> Well, it turns out you don't have to. With CheesyMock you can
 *    get an instance of HttpServletRequest just by writing something
 *    like this:</p>
 *
 * <pre>HttpServletRequest req = Cheesy.mock(HttpServletRequest.class);</pre>
 *
 * <p> Voila! One request instance coming up. But what about the
 *    return value? We need the call <code>req.getRequestURI()</code>
 *    to actually return something useful. Well, in that case we also
 *    need to supply a <i>delegate</i> object. If you call a method on
 *    the mock that's also defined in the delegate (i.e. the name,
 *    argument list and return type are equivalent), then the delegate
 *    method will be called and its return value will be returned from
 *    the mock method call.</p>
 *
 * <p> A delegate object can be any object at all, so for this case we
 *    define a new class inline:</p>
 *
 * <pre>HttpServletRequest req = Cheesy.mock(HttpServletRequest.class, new Object() {
 *    public String getRequestURI() {
 *       return "www.myserver.com/myApp/myServlet";
 *    }
 * });</pre>
 *
 * <p> Pretty neat, huh? But we can do even better. CheesyMock doesn't
 *    care about visibility modifiers on methods, so you can safely
 *    drop the <code>public</code>, which makes it a bit
 *    shorter. Also, CheesyMock supplies a very simple standard
 *    delegate called (you guessed it) {@link Delegate}, which can be
 *    used instead of {@link Object}. That in itself doesn't save any
 *    keystrokes, but Delegate has an alias (well, a subclass really)
 *    called simply {@link D}. Similarly, {@link Cheesy} also has an
 *    alias called {@link C} So the statement above could be written
 *    as:</p>
 *
 * <pre> HttpServletRequest req = C.mock(HttpServletRequest.class, new D() {
 *    String getRequestURI() {
 *       return "www.myserver.com/myApp/myServlet";
 *    }
 * });</pre>
 *
 * <p> Also, if you want it <i>really</i> short and sweet, you can
 *    take advantage of the JavaBean-style method accessors (see
 *    below) and just write:</p>
 *
 * <pre> HttpServletRequest req = C.mock(HttpServletRequest.class, new D() {
 *    String requestURI = "www.myserver.com/myApp/myServlet";
 * });</pre>
 *
 * <h2>JavaBean-style method accessors</h2>
 *
 * <p> CheesyMock recognizes JavaBean-style accessor methods in your
 *    interfaces and automatically tries to match them against the
 *    members of your delegate object. For example, if you have
 *    following interface:</p>
 *
 * <pre> public interface Example {
 *    public String getName();
 *    public void setName(String name);
 * }</pre>
 *
 * <p> then you will be able to declare your delegate as simple as
 * this:</p>
 *
 * <pre> Example mock = Cheesy.mock(Example.class, new Object() {
 *   String name = "foo";
 * });
 * mock.getName(); // returns "foo"
 * mock.setName("bar");
 * mock.getName(); // returns "bar"</pre>
 *
 * <h2>The check() method</h2>
 *
 * <p> CheesyMock employs a simple lifecycle model for mock objects:
 *
 * <ol>
 *
 * <li><p>Create the mock using {@link Cheesy#mock()} or other equivalent
 * method. During this stage you may get exceptions if your delegate
 * object is not accepted by the current set of validators. For
 * example, if you are using the 'strict' validators and implement a
 * method in your delegate that is not in the interface, you will get
 * an {@link java.lang.IllegalArgumentException}.</p></li>
 *
 * <li><p>Use the mock objects in your tests; call various methods, use
 * getters and setterns and so on. As above, the chosen validators may
 * throw exceptions. For example, with the 'strict' validators you
 * will get an {@link java.lang.UnsupportedOperationException} if you
 * call a method on the mock object that is not defined by the
 * delegate.</p></li>
 *
 * <li><p>Finally, after you are done with the mock object you may call
 * {@link Cheesy.check()} once to check its integrity. This is
 * optional, but highly recommended. As above, you may get exceptions
 * during this stage. For example, if you are using the 'strict'
 * validators you will get an exception if you have not called all
 * methods defined in the delegate object.</p>
 *
 * <p>Additionally, any method annotated with {@link Check} in the
 * delegate object will be invoked. This is a convenient way to
 * perform custom validation of the delegate object, for example
 * checking that the internal state is as expected.</p></li>
 *
 * </ol>
 *
 * <h2>Validators</h2>
 *
 * TBA
 *
 * <h2>Mocking regular java classes</h2>
 *
 * <p>CheesyMock can be used to create mock objects from regular java
 *    classes, something which is not possible using the dynamic proxy
 *    mechanism in the JDK. For this to work, you need a recent
 *    version of CGLIB (http://cglib.sourceforge.net/) on the class
 *    path at runtime.</p>
 *
 * <h2>Dependencies</h2>
 *
 * <p>Version 1.0 used log4j in some places, but for version 1.1 this
 *    dependency has been removed. Other than that, CheesyMock doesn't
 *    depend on any external jars at all.</p>
 *
 * <p>As mentioned above, you need CGLIB on the class path if you
 *    intend to mock regular classes. CheesyMock works fine without
 *    it, but you are then restricted to creating mocks from
 *    interfaces only.</p>
 *
 * @see C
 * @see Delegate
 * @see D
 * @author fredrik
 */
public class Cheesy {
	private static ProxyFactory factory = new SimpleProxyFactory();
	private static final DefaultReturn defaultReturn = new NullDefaultReturn();

	static {
		try {
			Class.forName("net.sf.cglib.proxy.Enhancer");
			factory = new CGLIBProxyFactory();
		}
		catch (ClassNotFoundException e) {
			// Did not find factory
		}
	}

	protected Cheesy() {
	}

	public static <T> T mock(Class<T> interfaceToImplement, Validator[] validators, Object delegate,
			DefaultReturn defaultReturn) {
		return (T) factory.newProxy(interfaceToImplement, validators, delegate, defaultReturn);
	}

	/**
	 * Create a mock object with a 'normal' set of validators.
	 */
	public static <T> T mock(Class<T> interfaceToImplement, Object delegate) {
		return mock(interfaceToImplement,
				new Validator[] {
					new StrictMethodOverride(),
					new StrictPropertyOverride(),
					new CheckDelegate()},
				delegate, defaultReturn);
	}

	/**
	 * Create a mock object with a 'normal' set of validators.
	 */
	public static <T> T mock(Class<T> interfaceToImplement) {
		return mock(interfaceToImplement, null);
	}


	/**
	 * Create a mock object with a 'strict' set of validators.
	 */
	public static <T> T mockStrict(Class<T> interfaceToImplement, Object delegate) {
		return mock(interfaceToImplement,
				new Validator[] {
					new StrictMethodOverride(),
					new StrictPropertyOverride(),
					new MandatoryInvocation(),
					new MissingMethod(),
					new CheckDelegate()},
				delegate, defaultReturn);
	}

	/**
	 * Create a mock object with a 'strict' set of validators.
	 */
	public static <T> T mockStrict(Class<T> interfaceToImplement) {
		return mockStrict(interfaceToImplement, null);
	}

	/**
	 * Create a mock object without any validation except for {@link CheckDelegate}.
	 */
	public static <T> T mockRelaxed(Class<T> interfaceToImplement, Object delegate) {
		return mock(interfaceToImplement,
				new Validator[] {
					new CheckDelegate()},
				delegate, defaultReturn);
	}

	/**
	 * Create a mock object without any validation except for {@link CheckDelegate}.
	 */
	public static <T> T mockRelaxed(Class<T> interfaceToImplement) {
		return mockRelaxed(interfaceToImplement, null);
	}

	/**
	 * Check the state of a previously instantiated and used mock object.
	 *
	 * @param mocks one or more mock objects
	 */
	public static void check(Object... mocks) {
		for(Object mock: mocks) {
			Object obj = factory.getHandler(mock);
			if (!(obj instanceof Mock)) {
				throw new IllegalArgumentException("Can only check objects of cheesymock.Mock");
			}
			try {
				((Mock)obj).check();
			}
			catch (RuntimeException e) {
				throw toRuntimeException(e);
			}
		}
	}

	public static ProxyFactory getProxyFactory() {
		return factory;
	}
}